iT邦幫忙

2023 iThome 鐵人賽

DAY 11
1

https://ithelp.ithome.com.tw/upload/images/20230915/20119486pz4ivGNyyh.png

前言

在上一篇文章中,我們初步建立了 MongoDB Atlas 的帳號,並且快速認識了 Mongoose,接下來當然就是要來整合再一起啦~

Express 與 Mongoose

首先一開始的一些起手式我就不再次說明了,直接附上指令讓你可以快速建立一個專案:

mkdir example-express-mongoose
cd example-express-mongoose
npm init -y
npm i express mongoose
touch index.js

理論上來說,上面的指令你應該沒有任何一行看不懂的,如果有,那就代表你沒有認真看前面的文章,建議你可以先回去看看~

https://ithelp.ithome.com.tw/upload/images/20230915/20119486vX4VPgNy2S.png

那麼我們這次要寫什麼呢?其實很簡單,把前面寫的範例程式碼把它整合到一起就好了,所以我們先來看看前面的程式碼:

const express = require('express');
const app = express();

const data = [];


app.use(express.json());

app.get('/todos', (req, res) => {
  res.send(data);
});

app.post('/todos', (req, res) => {
  const { title, completed } = req.body;

  data.push({
      id: new Date().getTime(),
      title: cacheBody.title,
      completed: cacheBody.completed,
   });

  res.send(data);
});

app.listen(3000)

接下來,補上我們前一篇所使用的 Mongoose 的程式碼:

const express = require('express');
const mongoose = require('mongoose');

const app = express();

// ...略過其他程式碼

mongoose.connect('mongodb+srv://example:example@cluster0.zyfzacs.mongodb.net/?retryWrites=true&w=majority')
  .then(() => console.log('Connected to MongoDB...'))

// ...略過其他程式碼

app.listen(3000)

當你在終端機輸入 npm start or node index.js 後,你會看到 Mongoose 已經幫你連上 MongoDB Atlas 了囉~

解決 Mongoose 連接問題

這時候有些人應該會有個好奇的疑問點...

「如果我在 Mongoose 連接成功之前,就已經有人來存取我的 API 了,那不是會出錯嗎?」

這個問題問得很好,確實是有這種可能性會發生,如果我們的專案在成功連接之前,就先接受了 API 請求,而剛好這些 API 請求需要操控資料庫的話,那麼確實是會出錯沒有錯。

那麼我們該怎麼解決這個麻煩的問題呢?其實我們只需要善加利用前面所學的 Middleware 就可以了,我們可以在 Middleware 中判斷是否已經連接成功,如果沒有的話,就回傳一個錯誤訊息,如果有的話,就繼續執行下一個 Middleware。

const express = require('express');
const mongoose = require('mongoose');
const app = express();

async function connectMongoDB () {
  try {
    await mongoose.connect('mongodb+srv://example:example@cluster0.zyfzacs.mongodb.net/?retryWrites=true&w=majority')
    console.log('Connected to MongoDB...')
    connectStatus = true;
  } catch (error) {
    console.log(error)
  }
}

connectMongoDB()

// ...略過其他程式碼

app.use(express.json());

app.use((req, res, next) => {
  if (connectStatus) {
    next();
  } else {
    res.status(503).send({
      status: false,
      message: 'Server is not ready'
    });
  }
})

// ...略過其他程式碼

app.listen(3000)

這樣子我們就可以避免在 Mongoose 還沒跟 MongoDB Atlas 連接成功之前,就先接受 API 請求的問題囉!

定義 Schema

在準備跟 MongoDB 互動(ex:CRUD)之前,我們必須先定義 Schema,這樣子才能夠讓 Mongoose 知道我們要存取的資料長什麼樣子。

const todoSchema = new mongoose.Schema({
  id: Number,
  title: String,
  completed: Boolean,
});

Note
CRUD 意指 Create、Read、Update、Delete,也就是我們常說的「增刪改查」。

那麼什麼是 Schema 呢?Schema 是用來定義 MongoDB 結構的一種概念,主要說明了這些資料的型別、欄位名稱、預設值等等,這樣子我們才能夠在存取資料時,知道該怎麼去存取。

因此以剛剛的範例程式碼來講,我們定義了一個 todoSchema,裡面有兩個欄位,分別是 titlecompleted,而這兩個欄位的型別分別是 String 跟 Boolean。

那麼只要是這些 Schema 屬性之外的資料,都會被忽略,也就是說,如果我們在存取資料時,傳入了一個多餘的欄位,那麼這個欄位就會被忽略,不會被存到資料庫中。

透過 Schema 可以確保我們資料的一致性和完整性。

定義 Model

定義完 Schema 之後,接著就是要來建立 Model,通常 Modal 會對應特定的 Schema,而且每個 Model 都會跟資料庫中的一個 collection 對應,也就是說,我們可以透過 Model 來存取資料庫中的資料。

const todoSchema = new mongoose.Schema({
  id: Number,
  title: String,
  completed: Boolean,
});

const Todo = mongoose.model('Todo', todoSchema);

你也可以把 mongoose.model 當作 new 實例化的概念沒有錯,因為它們的用法很像,只是 mongoose.model 是用來建立 Model 的,而 new 則是用來建立實例的。

透過這個 Modal 我們就可以針對資料庫做 CRUD(增刪改查)的動作囉!

存取資料庫與寫入資料

那麼目前我們完整程式碼長怎樣呢?我們先來看一下

const express = require('express');
const mongoose = require('mongoose');
const app = express();


let connectStatus = false;

async function connectMongoDB () {
  try {
    await mongoose.connect('mongodb+srv://example:example@cluster0.zyfzacs.mongodb.net/?retryWrites=true&w=majority')
    console.log('Connected to MongoDB...')
    connectStatus = true;
  } catch (error) {
    console.log(error)
  }
}

connectMongoDB()

const data = [];

app.use(express.json());

app.use((req, res, next) => {
  if (connectStatus) {
    next();
  } else {
    res.status(503).send({
      status: false,
      message: 'Server is not ready'
    });
  }
})

const todoSchema = new mongoose.Schema({
  id: Number,
  title: String,
  completed: Boolean,
});

const Todo = mongoose.model('Todo', todoSchema);

app.get('/todos', (req, res) => {
  res.send(data);
});

app.post('/todos', (req, res) => {
  const { title, completed } = req.body;

  data.push({
      id: new Date().getTime(),
      title: cacheBody.title,
      completed: cacheBody.completed,
   });

  res.send(data);
});

app.listen(3000)

我們第一個要做的事情是先來做 Post 的動作,也就是寫入資料庫

const express = require('express');

// ...略過其他程式碼

app.post('/todos', async (req, res) => {
  const { title, completed } = req.body;

  const todo = new Todo({
    id: new Date().getTime(),
    title,
    completed,
  });

  await todo.save();
  res.send({
    status: true,
    message: 'Create todo successfully',
  });
});

// ...略過其他程式碼

app.listen(3000)

其實我們可以發現實作難度真的不高,只要透過 new 來建立一個實例,然後透過 save 來存入資料庫就可以了。

取得 Todo 資料的話更簡單了,只需要透過 find 就可以取得資料庫中的資料囉!

const express = require('express');

// ...略過其他程式碼

app.get('/todos', async (req, res) => {
  const todos = await Todo.find();
  res.send({
    status: true,
    data: todos,
  });
});

// ...略過其他程式碼

app.listen(3000)

基本上以上程式碼你就可以透過 Postman 來測試了,不意外的話,當你打完 Post /todos 之後,你會發現資料庫中多了一筆資料,接著你在打 Get /todos 之後,你會得到以下資料

{
    "status": true,
    "data": [
        {
            "_id": "64e08a11f5e1d20be8115fb0",
            "id": 1692437009750,
            "title": "Hello",
            "completed": false,
            "__v": 0
        }
    ]
}

恭喜你,基本的新增與讀取的功能完成囉!

剩下的編輯與刪除的部分,我也想保留給你作為小功課,你可以試著自己實作看看,這邊我也補充相關資源讓你可以參考

以上資源我相信可以幫助你做出來的。

那麼這邊你應該會好奇 __v_id 這兩個屬性是什麼東西,這邊我也做一下補充。

這兩個屬性是 MongoDB 預設的屬性,_id 是 MongoDB 的主鍵(key),而 __v 則是版本鍵(version key),比較需要注意的是 _id 是 ObjectId,而不是我們一般的數字。

如果你希望不要有這兩個屬性,你可以透過 select 來過濾掉

const todos = await Todo.find().select('-__v -_id');

那麼這一篇差不多就先到這些結束,我們下一篇見。

碎碎念

終於來到第十一天了,其實我發現我身上的存稿真的不是很夠 Orz
而且我現在人正在日本發文唷~


上一篇
Day10 - MongoDB 與 Mongoose
下一篇
Day12 - 關於 CORS 與 Env
系列文
《Node.js 不負責系列:把前端人員當作後端來用,就算是前端也能嘗試寫的後端~原來 Node.js 可以做這麼多事~》31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言